package com.shenhaofeng.app.http.okhttp;

import com.shenhaofeng.app.http.CacheControl;
import com.shenhaofeng.app.http.ResponseCallback;
import com.shenhaofeng.app.http.Headers;
import com.shenhaofeng.app.http.HttpClient;
import com.shenhaofeng.app.http.RealCall;
import com.shenhaofeng.app.http.Result;
import com.shenhaofeng.app.http.ResultCallback;
import com.shenhaofeng.app.http.adapter.Adapter;
import com.shenhaofeng.app.http.request.FileBody;
import com.shenhaofeng.app.http.request.FormBody;
import com.shenhaofeng.app.http.request.MediaType;
import com.shenhaofeng.app.http.request.MultipartBody;
import com.shenhaofeng.app.http.request.Request;
import com.shenhaofeng.app.http.request.RequestBody;
import com.shenhaofeng.app.http.request.ValueBody;
import com.shenhaofeng.app.http.response.Protocol;
import com.shenhaofeng.app.http.response.Response;
import com.shenhaofeng.app.http.response.ResponseBody;
import com.shenhaofeng.app.http.type.Types;
import com.shenhaofeng.app.http.url.HttpUrl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import rx.Observable;
import rx.Subscriber;
import rx.functions.Func0;


/**
 * okhttp的实现
 * Created by Administrator on 2016/10/12.
 */
public class OkCall extends RealCall {


    private okhttp3.Call okCall;


    public OkCall(OkHttpCreator okHttpCreator, HttpClient httpClient, Request request) {
        super(httpClient, request);
        okCall = okHttpCreator.getOkHttpClient().newCall(Converter.request(request));
    }


    @Override
    public Response execute() throws Exception {
        okhttp3.Response okResponse = okCall.execute();
        return Converter.response(request(), okResponse);
    }


    @Override
    public <T> Result<T> execute(Types<T> type) {
        Adapter adapter = httpClient().creator().adapter();
        if (adapter == null) {
            throw new IllegalStateException("如果不指定数据适配器,那么必须在creator中配置全局的适配器");
        }
        return execute(type, adapter);
    }


    @Override
    public <T> Result<T> execute(Types<T> type, Adapter adapter) {
        if (adapter == null) {
            throw new IllegalArgumentException("适配器不能为空!");
        }
        Result.Builder<T> builder = new Result.Builder<>(type.getRawType());
        try {
            Response response = execute();
            if (response.isSuccessful()) {
                T data = adapter.adapt(type.getType(), response);
                builder.response(response)
                        .request(request())
                        .data(data);
            } else {
                builder.response(response)
                        .exception(new IllegalStateException(String.format("http code : %s", response.code())))
                        .request(request());
            }

        } catch (Exception e) {
            builder.request(request())
                    .exception(e);
        }
        return builder.build();
    }


    @Override
    public Observable<Response> response() {
        return Observable.defer(new Func0<Observable<Response>>() {
            @Override
            public Observable<Response> call() {
                return Observable.create(new Observable.OnSubscribe<Response>() {
                    @Override
                    public void call(Subscriber<? super Response> subscriber) {
                        try {
                            subscriber.onStart();
                            Response response = execute();
                            subscriber.onNext(response);
                            subscriber.onCompleted();
                        } catch (Exception e) {
                            subscriber.onError(e);
                        }
                    }
                });
            }
        });

    }


    @Override
    public <T> Observable<Result<T>> result(final Types<T> type) {
        Adapter adapter = httpClient().creator().adapter();
        if (adapter == null) {
            throw new IllegalStateException("如果不指定数据适配器,那么必须在creator中配置全局的适配器");
        }
        return result(type, adapter);
    }


    @Override
    public <T> Observable<Result<T>> result(final Types<T> type, final Adapter adapter) {
        if (adapter == null) {
            return Observable.error(new IllegalArgumentException("适配器不能为空!"));
        }
        return Observable.defer(new Func0<Observable<Result<T>>>() {
            @Override
            public Observable<Result<T>> call() {
                return Observable.create(new Observable.OnSubscribe<Result<T>>() {
                    @Override
                    public void call(Subscriber<? super Result<T>> subscriber) {
                        subscriber.onStart();
                        Result<T> result = execute(type, adapter);
                        subscriber.onNext(result);
                        subscriber.onCompleted();
                    }
                });
            }
        });
    }


    @Override
    public void enqueue(final ResponseCallback responseResponseCallback) {
        okCall.enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
                responseResponseCallback.onFailure(OkCall.this, e);
            }


            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
                responseResponseCallback.onResponse(OkCall.this, Converter.response(request(), response));
            }
        });
    }


    @Override
    public <T> void enqueue(final Types<T> type, final Adapter adapter, final ResultCallback resultCallback) {
        okCall.enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
                resultCallback.onFailure(OkCall.this, e);
            }


            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response okResponse) throws IOException {
                Response response = Converter.response(request(), okResponse);
                if (adapter == null) {
                    throw new IllegalArgumentException("适配器不能为空!");
                }
                Result.Builder<T> builder = new Result.Builder<>(type.getRawType());
                try {
                    if (response.isSuccessful()) {
                        T data = adapter.adapt(type.getType(), response);
                        builder.response(response)
                                .request(request())
                                .data(data);
                    } else {
                        builder.response(response)
                                .exception(new IllegalStateException(String.format("http code : %s", response.code())))
                                .request(request());
                    }

                } catch (Exception e) {
                    builder.request(request())
                            .exception(e);
                }
                resultCallback.onResult(OkCall.this, builder.build());
            }
        });
    }


    @Override
    public <T> void enqueue(Types<T> type, ResultCallback resultCallback) {
        Adapter adapter = httpClient().creator().adapter();
        if (adapter == null) {
            throw new IllegalStateException("如果不指定数据适配器,那么必须在creator中配置全局的适配器");
        }
        enqueue(type, adapter, resultCallback);
    }


    @Override
    public void cancel() {
        okCall.cancel();
    }


    @Override
    public boolean isExecuted() {
        return okCall.isExecuted();
    }


    @Override
    public boolean isCanceled() {
        return okCall.isCanceled();
    }


    static class Converter {

        /**
         * 映射请求
         *
         * @param request
         * @return
         */
        static okhttp3.Request request(Request request) {
            okhttp3.Request.Builder okRequestBuilder = new okhttp3.Request.Builder();
            okRequestBuilder.method(request.method(), requestBody(request.requestBody()));
            okRequestBuilder.url(httpUrl(request.url()));
            okRequestBuilder.cacheControl(cacheControl(request.cacheControl()));
            okRequestBuilder.headers(headers(request.headers()));
            return okRequestBuilder.build();
        }


        /**
         * 映射响应
         *
         * @param response
         * @return
         */
        static Response response(Request request, okhttp3.Response response) {
            Protocol protocol = new Protocol(response.protocol().toString());
            Response.Builder responseBuilder = new Response.Builder();
            responseBuilder.code(response.code())
                    .request(request)
                    .protocol(protocol)
                    .headers(headers(response.headers()));
            responseBuilder.body(responseBody(response.body()));
            // TODO: 2016/10/13 缓存
            return responseBuilder.build();
        }


        static okhttp3.RequestBody requestBody(RequestBody body) {
            if (body == null) {
                return null;
            }
            if (FormBody.class.isInstance(body)) {
                FormBody formBody = FormBody.class.cast(body);
                okhttp3.FormBody.Builder builder = new okhttp3.FormBody.Builder();
                for (int i = 0; i < formBody.size(); i++) {
                    builder.addEncoded(formBody.getEncodedName(i), formBody.getEncodedValue(i));
                }
                return builder.build();
            } else if (MultipartBody.class.isInstance(body)) {
                MultipartBody multipartBody = MultipartBody.class.cast(body);
                okhttp3.MultipartBody.Builder okMultipartBodyBuilder = new okhttp3.MultipartBody.Builder(multipartBody.boundary())
                        .setType(Converter.mediaType(multipartBody.contentType()));
                List<MultipartBody.Part> entities = multipartBody.entities();
                for (int i = 0; i < entities.size(); i++) {
                    MultipartBody.Part part = entities.get(i);
                    okhttp3.MultipartBody.Part okPart = part(part);
                    okMultipartBodyBuilder.addPart(okPart);
                }
                return okMultipartBodyBuilder.build();
            } else if (FileBody.class.isInstance(body)) {
                FileBody fileBody = FileBody.class.cast(body);
                return okhttp3.RequestBody.create(mediaType(fileBody.contentType()), fileBody.file());
            } else if (ValueBody.class.isInstance(body)) {
                ValueBody valueBody = ValueBody.class.cast(body);
                return okhttp3.RequestBody.create(null, valueBody.value());
            } else {
                throw new IllegalStateException("unknown body type!");
            }
        }


        static ResponseBody responseBody(okhttp3.ResponseBody responseBody) {
            return new OkResponseBody(responseBody);
        }


        static okhttp3.HttpUrl httpUrl(HttpUrl body) {
            return okhttp3.HttpUrl.parse(body.getUrl());
        }


        static okhttp3.CacheControl cacheControl(CacheControl cacheControl) {
            if (cacheControl == null) {
                return new okhttp3.CacheControl.Builder().build();
            }
            okhttp3.CacheControl.Builder builder = new okhttp3.CacheControl.Builder();
            cacheControl.sMaxAgeSeconds();
            if (cacheControl.maxAgeSeconds() >= 0) {
                builder.maxAge(cacheControl.maxAgeSeconds(), TimeUnit.SECONDS);
            }
            if (cacheControl.maxStaleSeconds() >= 0) {
                builder.maxStale(cacheControl.maxStaleSeconds(), TimeUnit.SECONDS);
            }
            if (cacheControl.minFreshSeconds() >= 0) {
                builder.minFresh(cacheControl.minFreshSeconds(), TimeUnit.SECONDS);
            }
            if (cacheControl.noCache()) {
                builder.noCache();
            }
            if (cacheControl.noStore()) {
                builder.noStore();
            }
            if (cacheControl.noTransform()) {
                builder.noTransform();
            }
            return builder.build();

        }


        static okhttp3.Headers headers(Headers headers) {
            if (headers == null) {
                return okhttp3.Headers.of();
            }
            return okhttp3.Headers.of(headers.namesAndValues());
        }


        static Headers headers(okhttp3.Headers headers) {
            if (headers == null) {
                return Headers.create();
            }
            List<String> nvs = new ArrayList<>();
            for (int i = 0; i < headers.size(); i++) {
                nvs.add(headers.name(i));
                nvs.add(headers.value(i));
            }
            return Headers.create(nvs.toArray(new String[0]));
        }


        static okhttp3.MediaType mediaType(MediaType mediaType) {
            if (mediaType == null) {
                return null;
            }
            return okhttp3.MediaType.parse(mediaType.getValue());
        }


        static okhttp3.MultipartBody.Part part(MultipartBody.Part part) {
            if (part == null) {
                return null;
            }
            if (part.name() != null) {
                return okhttp3.MultipartBody.Part.createFormData(part.name(), part.filename(), requestBody(part.requestBody()));
            } else {
                return okhttp3.MultipartBody.Part.create(headers(part.headers()), requestBody(part.requestBody()));
            }
        }


    }
}
